/*
 * multidimensional simulation code based on nvidia OpenCL / OpenGL interop example
 *
 */


#ifdef _WIN32
#  define WINDOWS_LEAN_AND_MEAN
#  define NOMINMAX
#  include <windows.h>
#endif

// OpenGL Graphics Includes
#include <GL/glew.h>
#if defined (__APPLE__) || defined(MACOSX)
#include <OpenGL/OpenGL.h>
#include <GLUT/glut.h>
#else
#include <GL/freeglut.h>
#ifdef UNIX
#include <GL/glx.h>
#endif
#endif

// Includes
#include <memory>
#include <iostream>
#include <cassert>

// Utilities, OpenCL and system includes
#include <oclUtils.h>
#include <shrQATest.h>

#if defined (__APPLE__) || defined(MACOSX)
#define GL_SHARING_EXTENSION "cl_APPLE_gl_sharing"
#else
#define GL_SHARING_EXTENSION "cl_khr_gl_sharing"
#endif

//My additions
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <algorithm>
#include <stdlib.h>
#include <assert.h>
#include <cstdio>
#include <string>

#define REFRESH_DELAY	  10 //ms
#define GAP  25 // gap between subwindows

using namespace std;

#ifndef __linux__ 
const unsigned int window_width = 1024;
const unsigned int window_height = 768;
#endif

#ifdef __linux__
const unsigned int window_width = 1366*6;
const unsigned int window_height = 768*3;
#endif

//GLOBAL OPENCL stuff
const char* cExecutableName = NULL;
shrBOOL bQATest = shrFALSE;
shrBOOL bNoPrompt = shrFALSE;  
cl_int ciErrNum;
void Cleanup(int iExitCode);
void (*pCleanup)(int) = &Cleanup;
cl_platform_id cpPlatform;
cl_uint uiDevCount;
cl_device_id* cdDevices;
cl_context cxGPUContext;
char* cPathAndName = NULL;          // var for full paths to data, src, etc.
char* cSourceCL = NULL;             // Buffer to hold source for compilation 
cl_program cpProgram;

int devicesThatCouldBeUsed[10];// in the unlikely event that there are 10 gpus ...
int numDevicesThatCouldBeUsed;
cl_context cxGPUContexts[10];

int computationHeight = 1;
const int numFish = 1024;
int vertPerFish =  3;

float minEyeX, minEyeY, minEyeZ, maxEyeX, maxEyeY, maxEyeZ, eyeX, eyeY, eyeZ;//for zoom

int minX = -500;
int maxX = 2500;
int minY = -500;
int maxY = 2500;
int minZ = 0;
int maxZ = 4000;

float deltaX = 0.0;
float deltaY = 0.0;
float deltaZ = 0.0;

// mouse controls
int mouse_old_x, mouse_old_y;
int mouse_buttons = 0;
float rotate_x = 0.0, rotate_y = 0.0;

//handle speed of simulations
float velLevel = 1;
float oldVelLevel = 1;
bool changed = false;

//targets
float target1[4];
float target2[4];

bool drawMenu = false; 

int minInformedX = 0;
int maxInformedX = 2;
int minInformedY = 0;
int maxInformedY = 2;

//**************************************** SUBWINDOW CLASS ****************************************

class SubWin
{    
public: 
    SubWin( int mainWin, int subwindowId, int subWinX, int num, int subWinY, int subWinW, int subWinH );
    ~SubWin();
    
    //OpenGL
    void display(void);
    void drawFish(int, int, int, int);
    void reshape(int w, int h);
    
    //GLSL
    GLuint shaderProg;
    char *textFileRead(char *fn);
    void printShaderLog(GLuint prog);
    void printProgramLog(GLuint shad);
    GLuint setShaders(char * vert, char * frag, char * geom);
    
    GLuint vbo;//holds vertex data- position of vertices and color
    void createVBO(GLuint* vbo);
    
    void createFish( float fpos [numFish][4], float fvel[numFish][4], float fschoolingData[numFish][4], float percInformed1, float percInformed2);//bool isInformed[numFish][1], float fInformedData[numFish][4] );
    
    float fishPos[numFish][4];
    float fishVel[numFish][4]; //array of float 4's to hold vx, vy, vz and nothing (for now) - 
    float fishSchoolingData[numFish][4];//array of float 4's to hold coh,sep,ali,perceptualRange
    bool isInformed[numFish][1];//holds true if is an informed fish.  false if not
    float fishInformedData[numFish][4];// targetx, targety, targetz, strength

    float * h_fishPosData;//my note: host -- can copy between
    float * h_fishVelData; 
    float * h_fishSchoolingData;
    float * h_fishInformedData;
    bool * h_isInformed;

    unsigned int anim;
    
    //OpenCL
    cl_kernel ckKernel;
    cl_command_queue cqCommandQueue;
    cl_mem vbo_cl;
    cl_mem cl_fishPosData;//ptr to buffer data for the fish
    cl_mem cl_fishVelData;//ptr to buffer data for the fish
    cl_mem cl_fishSchoolingData;//ptr to buffer data for the fish
    cl_mem cl_fishInformedData;//ptr to buffer data for the fish
    cl_mem cl_isInformed;
    unsigned int uiDeviceUsed;//init
    void setUIDeviceUsed( unsigned int u );
    void setupOpenCL(); 
    void resetCLData();
    void runKernel();
    float globalKernelTime;// = 0.0f;
    void cleanup();
    
    //Location and subwindow info
    int subWinId;
    int subWinX;
    int subWinY;
    int subWinW;
    int subWinH;
    
    int mainWinId;//parent window
    
    int num;//which subwindow is it
    
    int numInformedTarget1;
    int numInformedTarget2;
    void renderBitmapString(float x, float y, void *font, const char *string);
    void setOrthographicProjection();
    void restorePerspectiveProjection();
    void drawLabel();
    
};

SubWin::SubWin( int mainWin, int swId, int n, int x, int y, int w, int h )
{
    num = n;
    subWinX = x;
    subWinY = y;
    subWinW = w;
    subWinH = h;
    mainWinId = mainWin;
    subWinId = swId;
    uiDeviceUsed = 0;//will be reset later
    globalKernelTime = 0.0f;
    
    // default initialization
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glDisable(GL_DEPTH_TEST);
    
    // viewport
    glViewport( subWinX, subWinY, subWinW, subWinH );//0, 0, window_width, window_height);
    
    // projection
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, (GLfloat)window_width / (GLfloat) window_height, 0.1, 11000.0);
    gluLookAt(eyeX, eyeY, eyeZ,    eyeX, eyeY, 0,    0, 1, 0);
    
    anim = 0;
    
    numInformedTarget1 = 0;
    numInformedTarget2 = 0;

}

void SubWin::setUIDeviceUsed(unsigned int u )
{
    uiDeviceUsed = u;
}

void SubWin::setupOpenCL()
{
    std::cout << "setting up opencl in subwin " << num << std::endl;
    
    // initialize the fish for the kernel
    h_fishPosData = (float *) malloc (4* (numFish) * sizeof(float));//4 floats for each fish x, y, z, w  
    h_fishVelData = (float *) malloc (4* (numFish) * sizeof(float));//4 floats for each fish vx, vy, vz, vw
    h_fishSchoolingData = (float *) malloc (4* (numFish) * sizeof(float));//4 floats for each fish
    h_fishInformedData = (float *) malloc (4* (numFish) * sizeof(float));//4 floats for each fish
    h_isInformed = ( bool * ) malloc( numFish*sizeof(bool) ); 
    int fCounter, i;
    for (fCounter = 0; fCounter < numFish; fCounter ++)
    {
		for (i=0; i< 4; i++)
        {
	    	h_fishPosData[4 * fCounter + i] = fishPos[fCounter][i];//make into 1d array            
	    	h_fishVelData[4 * fCounter + i] = fishVel[fCounter][i];//make into 1d array
            h_fishSchoolingData[4 * fCounter + i] = fishSchoolingData[fCounter][i];
            h_fishInformedData[4 * fCounter + i] = fishInformedData[fCounter][i];
        }
        
        h_isInformed[ fCounter ] = isInformed[fCounter][0];
    }
    
    // combine creation of memory on the device and copying values over
    cl_fishPosData = clCreateBuffer(cxGPUContext, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
                                    4 * (numFish) * sizeof(float), h_fishPosData, &ciErrNum);//gives back ptr to buffer
    oclCheckError(ciErrNum, CL_SUCCESS);
    
    cl_fishVelData = clCreateBuffer(cxGPUContext, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
                                    4 * (numFish) * sizeof(float), h_fishVelData, &ciErrNum);//gives back ptr to buffer
    oclCheckError(ciErrNum, CL_SUCCESS);
    
    cl_fishSchoolingData = clCreateBuffer(cxGPUContext, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
                                          4 * (numFish) * sizeof(float), h_fishSchoolingData, &ciErrNum);//gives back ptr to buffer
    oclCheckError(ciErrNum, CL_SUCCESS);
    
    cl_fishInformedData = clCreateBuffer(cxGPUContext, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
                                         4 * (numFish) * sizeof(float), h_fishInformedData, &ciErrNum);//gives back ptr to buffer
    oclCheckError(ciErrNum, CL_SUCCESS);
    
    cl_isInformed = clCreateBuffer(cxGPUContext, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
                                   4 * (numFish) * sizeof(bool), h_fishInformedData, &ciErrNum);//gives back ptr to buffer
    oclCheckError(ciErrNum, CL_SUCCESS);
    
    // create the kernel
    ckKernel = clCreateKernel(cpProgram, "soManyFish", &ciErrNum);
    shrCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
    
    // create a command-queue use 0 for no profiling or CL_QUEUE_PROFILING_ENABLE to enable profiling
    cqCommandQueue = clCreateCommandQueue(cxGPUContext, cdDevices[uiDeviceUsed], CL_QUEUE_PROFILING_ENABLE, &ciErrNum);
    shrCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
    
    // if there is a problem creating the kernel print out some detailed information about it
    if (ciErrNum) {
   		char log[10240] = "";
   		ciErrNum = clGetProgramBuildInfo(cpProgram, cdDevices[uiDeviceUsed], CL_PROGRAM_BUILD_LOG, sizeof(log), log, NULL);
   		fprintf(stderr, "Error(s) creating the kernel:\n%s\n", log);
    }
	shrCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
    
    // create VBO (if using standard GL or CL-GL interop), otherwise create Cl buffer
    createVBO(&vbo);//holds graphical representation of the fish-- separate from data representation
    
    int computationWidth = numFish/computationHeight;
    
    // set the args values  for kernel-- will get any error- note, order and type must matter- still one param left
    ciErrNum  = clSetKernelArg(ckKernel, 0, sizeof(cl_mem), (void *) &vbo_cl);//ptr to vertex buff
    ciErrNum |= clSetKernelArg(ckKernel, 1, sizeof(cl_mem), (void *) &cl_fishPosData); //ptr to mem buf on card    
    ciErrNum |= clSetKernelArg(ckKernel, 2, sizeof(cl_mem), (void *) &cl_fishVelData); //ptr to mem buf on card
    ciErrNum |= clSetKernelArg(ckKernel, 3, sizeof(cl_mem), (void *) &cl_fishSchoolingData); //ptr to mem buf on card
    ciErrNum |= clSetKernelArg(ckKernel, 4, sizeof(cl_mem), (void *) &cl_fishInformedData); //ptr to mem buf on card
    ciErrNum |= clSetKernelArg(ckKernel, 5, sizeof(cl_mem), (void *) &cl_isInformed); //ptr to mem buf on card    
    ciErrNum |= clSetKernelArg(ckKernel, 6, sizeof(unsigned int), (void *) &computationWidth);
    ciErrNum |= clSetKernelArg(ckKernel, 7, sizeof(unsigned int), (void *) &computationHeight);
    ciErrNum |= clSetKernelArg(ckKernel, 8, sizeof(unsigned int), (void *) &numFish);
    
    shrCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
	// note that there is one more kernel argument set later since it changes each time through the loop

    std::cout << "done with kernal and prog setup in subwindow " << num << std::endl;
    
    //this really doesn't fit with the theme of the function, but it has to go here
    if(!bQATest) 
    {
        //init GLSL
        shaderProg = setShaders((char *) "./vertexShader.vert", (char *) "./fragShader.frag", (char *) "./geomShader.geom");
    }

}

void SubWin::resetCLData()
{
    //first cleanup
    if(cl_fishPosData)clReleaseMemObject(cl_fishPosData);
    if(cl_fishVelData)clReleaseMemObject(cl_fishVelData);
    if(cl_fishSchoolingData)clReleaseMemObject(cl_fishSchoolingData);
    if(cl_fishInformedData)clReleaseMemObject(cl_fishInformedData);
    if(cl_isInformed)clReleaseMemObject(cl_isInformed);
    
    int computationWidth = numFish/computationHeight;

    int fCounter, i;
    for (fCounter = 0; fCounter < numFish; fCounter ++)
    {
		for (i=0; i< 4; i++)
        {
	    	h_fishPosData[4 * fCounter + i] = fishPos[fCounter][i];//make into 1d array            
	    	h_fishVelData[4 * fCounter + i] = fishVel[fCounter][i];//make into 1d array
            h_fishSchoolingData[4 * fCounter + i] = fishSchoolingData[fCounter][i];
            h_fishInformedData[4 * fCounter + i] = fishInformedData[fCounter][i];
        }
        
        h_isInformed[ fCounter ] = isInformed[fCounter][0];
    }
    
    anim = 0;
    
    // combine creation of memory on the device and copying values over
    cl_fishPosData = clCreateBuffer(cxGPUContext, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
                                    4 * (numFish) * sizeof(float), h_fishPosData, &ciErrNum);//gives back ptr to buffer
    oclCheckError(ciErrNum, CL_SUCCESS);
    
    cl_fishVelData = clCreateBuffer(cxGPUContext, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
                                    4 * (numFish) * sizeof(float), h_fishVelData, &ciErrNum);//gives back ptr to buffer
    oclCheckError(ciErrNum, CL_SUCCESS);
    
    cl_fishSchoolingData = clCreateBuffer(cxGPUContext, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
                                          4 * (numFish) * sizeof(float), h_fishSchoolingData, &ciErrNum);//gives back ptr to buffer
    oclCheckError(ciErrNum, CL_SUCCESS);
    
    cl_fishInformedData = clCreateBuffer(cxGPUContext, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
                                         4 * (numFish) * sizeof(float), h_fishInformedData, &ciErrNum);//gives back ptr to buffer
    oclCheckError(ciErrNum, CL_SUCCESS);
    
    cl_isInformed = clCreateBuffer(cxGPUContext, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
                                   4 * (numFish) * sizeof(bool), h_fishInformedData, &ciErrNum);//gives back ptr to buffer
    oclCheckError(ciErrNum, CL_SUCCESS);
    
    
    // set the args values  for kernel-- will get any error- note, order and type must matter- still one param left
    ciErrNum  = clSetKernelArg(ckKernel, 0, sizeof(cl_mem), (void *) &vbo_cl);//ptr to vertex buff
    ciErrNum |= clSetKernelArg(ckKernel, 1, sizeof(cl_mem), (void *) &cl_fishPosData); //ptr to mem buf on card    
    ciErrNum |= clSetKernelArg(ckKernel, 2, sizeof(cl_mem), (void *) &cl_fishVelData); //ptr to mem buf on card
    ciErrNum |= clSetKernelArg(ckKernel, 3, sizeof(cl_mem), (void *) &cl_fishSchoolingData); //ptr to mem buf on card
    ciErrNum |= clSetKernelArg(ckKernel, 4, sizeof(cl_mem), (void *) &cl_fishInformedData); //ptr to mem buf on card
    ciErrNum |= clSetKernelArg(ckKernel, 5, sizeof(cl_mem), (void *) &cl_isInformed); //ptr to mem buf on card    
    ciErrNum |= clSetKernelArg(ckKernel, 6, sizeof(unsigned int), (void *) &computationWidth);
    ciErrNum |= clSetKernelArg(ckKernel, 7, sizeof(unsigned int), (void *) &computationHeight);
    ciErrNum |= clSetKernelArg(ckKernel, 8, sizeof(unsigned int), (void *) &numFish);
}

void SubWin::createVBO(GLuint* vbo)
{
    // create VBO
    unsigned int size = ((numFish) * 8 * sizeof( float ) * vertPerFish); //4 position, 4 color  
    //-- why 8? x, y, z, w, r, g, b, a-- visual params
    
    if(!bQATest)
    {
        // create buffer object
        glGenBuffers(1, vbo);
        glBindBuffer(GL_ARRAY_BUFFER, *vbo);
        
        // initialize buffer object
        glBufferData(GL_ARRAY_BUFFER, size, 0, GL_DYNAMIC_DRAW);
        
#ifdef GL_INTEROP
        // create OpenCL buffer from GL VBO
        vbo_cl = clCreateFromGLBuffer(cxGPUContext, CL_MEM_WRITE_ONLY, *vbo, NULL);
#else
        // create standard OpenCL mem buffer
        vbo_cl = clCreateBuffer(cxGPUContext, CL_MEM_WRITE_ONLY, size, NULL, &ciErrNum);
#endif
        shrCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
    }
    else 
    {
        // create standard OpenCL mem buffer
        vbo_cl = clCreateBuffer(cxGPUContext, CL_MEM_WRITE_ONLY, size, NULL, &ciErrNum);
        shrCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
    }

}

void SubWin::runKernel()
{
 	size_t szGlobalWorkSize[] = {(numFish)/computationHeight, computationHeight};
    
    ciErrNum = CL_SUCCESS;
    
#ifdef GL_INTEROP   
    // map OpenGL buffer object for writing from OpenCL
    glFinish();
    ciErrNum = clEnqueueAcquireGLObjects(cqCommandQueue, 1, &vbo_cl, 0,0,0);
    shrCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
    
#endif
    
    cl_event eventGlobal;
    cl_int errcode_ret;
    
    // Set args and execute the kernel
    ciErrNum |= clSetKernelArg(ckKernel, 9, sizeof(unsigned int), &anim);//set final argument- counter
    //if( changed )
    //{
    ciErrNum |= clSetKernelArg(ckKernel, 10, sizeof(float), (void *) &velLevel);
    changed = false;
    //}
    
    //enque the kernel
    ciErrNum |= clEnqueueNDRangeKernel(cqCommandQueue, ckKernel, 2, NULL, szGlobalWorkSize, NULL, 0, 0, &eventGlobal );
    cl_ulong end, start;
    
    // lets do some profiling
    errcode_ret = clWaitForEvents(1, &eventGlobal);
    oclCheckError(errcode_ret, CL_SUCCESS);
    errcode_ret = clGetEventProfilingInfo(eventGlobal, CL_PROFILING_COMMAND_END, sizeof(cl_ulong), &end, 0);
    errcode_ret |= clGetEventProfilingInfo(eventGlobal, CL_PROFILING_COMMAND_START, sizeof(cl_ulong), &start, 0);
    oclCheckError(errcode_ret, CL_SUCCESS);
    //fprintf(stderr, " %2i , Global kernel time: %0.3f ms\n", num , (end-start)*1.0e-6f);
    globalKernelTime = (end-start)*1.0e-6f;
    
    shrCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
    
#ifdef GL_INTEROP
    // unmap buffer object
    ciErrNum = clEnqueueReleaseGLObjects(cqCommandQueue, 1, &vbo_cl, 0,0,0);
    shrCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
    clFinish(cqCommandQueue);
#else

    // Explicit Copy 
    // map the PBO to copy data from the CL buffer via host
    glBindBufferARB(GL_ARRAY_BUFFER, vbo);  
    
    // map the buffer object into client's memory
    void* ptr = glMapBufferARB(GL_ARRAY_BUFFER, GL_WRITE_ONLY_ARB);
    if( ptr == NULL )
        cout << "pointer is null " << endl;
    ciErrNum = clEnqueueReadBuffer(cqCommandQueue, vbo_cl, CL_TRUE, 0, 8 * numFish * sizeof(float) * vertPerFish, ptr, 0, NULL, NULL);
    if( ciErrNum == CL_INVALID_COMMAND_QUEUE  )
        cout << "invalid command queue" << endl;
    else if( ciErrNum == CL_INVALID_CONTEXT  )
        cout << "invalid context" << endl;
    else if( ciErrNum == CL_INVALID_MEM_OBJECT  )
        cout << "invalid mem object" << endl;
    else if( ciErrNum == CL_INVALID_VALUE  )
        cout << "invalid value  " << num << endl;
    shrCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);

    //clFinish( cqCommandQueue );
    
    //delete ptr;
    
    glUnmapBufferARB(GL_ARRAY_BUFFER); 
#endif    
}


void SubWin::display(void)
{
    anim += 1;
    
    drawFish(subWinX, subWinY, subWinW, subWinH);
    
}

void SubWin::drawFish( int x, int y, int w, int h )
{
    // viewport
    glViewport(x, y, w, h);
        
    // projection
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, w / h, 0.1, 11000.0);//60.0, (GLfloat)window_width / (GLfloat) window_height, 0.1, 11000.0);
    gluLookAt(eyeX, eyeY, eyeZ,    eyeX, eyeY, 0,    0, 1, 0);
    
    // run OpenCL kernel to generate vertex positions
    runKernel();//will put info in the vertex buffer
    //draw what comes back
    
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef( 1000.0, 1000.0, 1000.0 );
    glTranslatef( deltaX, deltaY, deltaZ );
    glRotatef(rotate_x, 1.0, 0.0, 0.0);//rotate with mouse
    glRotatef(rotate_y, 0.0, 1.0, 0.0);
    glTranslatef( -1000.0, -1000.0, -1000.0);

    
    //-------DRAW FISH------------
    glUseProgram(shaderProg);
    
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glVertexPointer(4, GL_FLOAT, 0, 0);//use this vert buffer, the first 4 of which are location
    glColorPointer(4, GL_FLOAT, 0, (GLvoid *) ((numFish) * sizeof(float)*4*vertPerFish));//why 3- because 3 pts per triangle
    
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);
    
    glPointSize(3.0);
    glColor4f( 1.0, 0.0, 0.0, 1.0);
    glDrawArrays(GL_TRIANGLES, 0, (numFish)*vertPerFish);
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);
    
    glBegin(GL_POINTS);
    glEnd();
    
    glFlush();
    
    glUseProgram(0);
    
    glPointSize( 6.0 );
    glBegin(GL_POINTS);
    glColor4f( 1.0, 0.0, 0.0, 1.0);
    glVertex4f( target1[0], target1[1], target1[2], target1[3] );
    glColor4f( 1.0, 1.0, 0.0, 1.0);
    glVertex4f( target2[0], target2[1], target2[2], target2[3] );
    glEnd();
    
    glColor4f( 1.0, 1.0, 1.0, 1.0);
    glTranslatef( 1000, 1000, 1000 );
    glutWireCube( 2000 );
    
    drawLabel();
    
}


void SubWin::reshape(int width, int height)
{
    //  Represents a side of the viewport. A viewport is intended to
    //  to take a square shape so that the aspect ratio is reserved
    int viewport_side = 0;
    
    //  Viewport x and y positions (Center viewport)
    int viewport_x = 0, viewport_y = 0;
    
    //  Calculate viewport side
    viewport_side = (width > height) ? height : width;
    
    //  Calculate viewport position
    viewport_x = (width - viewport_side) / 2;
    viewport_y = (height - viewport_side) / 2;
    
    //  Preserve aspect ratio
    glViewport (viewport_x, viewport_y, viewport_side, viewport_side);
    
    int deltaX = width - subWinW;
    int deltaY = height - subWinH; 
    
    subWinX -= deltaX/2.0;
    subWinY -= deltaY/2.0;
    
    //  Set subwindow width and height
    subWinW = width;
    subWinH = height;
    
    //  Notify that we are reshaping subwindow 1
    printf ("Subwindow %1i ", num);
    
    //  Print current width and height
    printf ("Width: %d, Height: %d, Viewport Side: %d.\n", width, height, viewport_side);
}

void SubWin::createFish(float fpos [numFish][4], float fvel[numFish][4], float fschoolingData[numFish][4], float percInformed1, float percInformed2 )
{
    std::cout << "sub window create fish " << numFish << std::endl;
    //for random number generation
    srand ( time(NULL) );
    
    for(int i = 0; i < numFish; i++)
    {
        fishPos[i][0] = fpos[i][0];//(rand() % 100 );
        fishPos[i][1] = fpos[i][1];//(rand() % 100 );
        fishPos[i][2] = fpos[i][2];//(rand() % 100 );
        fishPos[i][3] = fpos[i][3];//1.0;
        
        fishVel[i][0] = fvel[i][0];//((rand() % 5 ))*.1;// + .25; //TEST
        fishVel[i][1] = fvel[i][1];//((rand() % 5 ))*.1;// + .25;
        fishVel[i][2] = fvel[i][2];//((rand() % 5 ))*.1;// + .25;
        fishVel[i][3] = fvel[i][3];//0.0; //wv
        
        fishSchoolingData[i][0] = fschoolingData[i][0];//.001; //cohesion
        fishSchoolingData[i][1] = fschoolingData[i][1];//.016; //alignment
        fishSchoolingData[i][2] = fschoolingData[i][2];//1.0; //separation
        fishSchoolingData[i][3] = fschoolingData[i][3];//3.0; //perceptual range- number of body lengths
        
        //setup informed data-- NOT COPYING FROM THE GIVEN DATA
        
        float p1 = (float)percInformed1/100.0;
        float p2 = (float)percInformed2/100.0;
        
        numInformedTarget1 = (int)((float)numFish*p1);
        numInformedTarget2 = (int)((float)numFish*p2);
        
        cout << "perc 1 " << p1 << " " << (float)(numFish*p1) << " perc 2 " << p2 << " " << ((float)numFish*p2) << endl;
        if( i < (float)(numFish*p1) )//i % 1000 == 0 )//isInformed[i][0] )
        {
            fishInformedData[i][0] = target1[0];//500.0;//tz
            fishInformedData[i][1] = target1[1];//500.0;//ty
            fishInformedData[i][2] = target1[2];//500.0;//tx
            fishInformedData[i][3] = .0001; //strength
            
            isInformed[i][0] = true;; // 50% are informed
        }
        else if( i < ((float)numFish*p1)+((float)numFish*p2) )
        {
            fishInformedData[i][0] = target2[0];//500.0;//tz
            fishInformedData[i][1] = target2[1];//500.0;//ty
            fishInformedData[i][2] = target2[2];//500.0;//tx
            fishInformedData[i][3] = .0001; //strength
            
            isInformed[i][0] = true;
        } 
        else
        {
            fishInformedData[i][0] = -1.0;//tz
            fishInformedData[i][1] = -1.0;//ty
            fishInformedData[i][2] = -1.0;//tx
            fishInformedData[i][3] = 0.0; //strength
            
            isInformed[i][0] = false;

        }
    }
    
}


/////////////////////////////////---GLSL----/////////////////////////////////////
char * SubWin::textFileRead(char *fn)
{
	FILE *fp;
	char *content = NULL;
    
	int count=0;
    
	if (fn != NULL) {
		fp = fopen(fn,"rt");
        
		if (fp != NULL) {
            
			fseek(fp, 0, SEEK_END);
			count = ftell(fp);
			rewind(fp);
            
			if (count > 0) {
				content = (char *)malloc(sizeof(char) * (count+1));
				count = fread(content,sizeof(char),count,fp);
				content[count] = '\0';
			}
			fclose(fp);
		}
	}
	
	if (content == NULL)
    {
        fprintf(stderr, "ERROR: could not load in file %s\n", fn);
        exit(1);
    }
	return content;
} 

void SubWin::printShaderLog(GLuint prog)
{
    GLint infoLogLength = 0;
    GLsizei charsWritten  = 0;
    GLchar *infoLog;
    
    glGetShaderiv(prog, GL_INFO_LOG_LENGTH, &infoLogLength);
    
    if (infoLogLength > 0)
    {
        infoLog = (char *) malloc(infoLogLength);
        glGetShaderInfoLog(prog, infoLogLength, &charsWritten, infoLog);
		printf("%s\n",infoLog);
        free(infoLog);
    }
}

void SubWin::printProgramLog(GLuint shad)
{
    GLint infoLogLength = 0;
    GLsizei charsWritten  = 0;
    GLchar *infoLog;
    
    glGetProgramiv(shad, GL_INFO_LOG_LENGTH, &infoLogLength);
    
    if (infoLogLength > 0)
    {
        infoLog = (char *) malloc(infoLogLength);
        glGetProgramInfoLog(shad, infoLogLength, &charsWritten, infoLog);
		printf("%s\n",infoLog);
        free(infoLog);
    }
}


GLuint SubWin::setShaders(char * vert, char * frag, char * geom) {//vert frag and geom
	GLuint v,f, g, pro;
	char *vs, *fs, *gs;
	
	v = glCreateShader(GL_VERTEX_SHADER);
	f = glCreateShader(GL_FRAGMENT_SHADER);
	g = glCreateShader(GL_GEOMETRY_SHADER_EXT);
	
	vs = textFileRead(vert);
	fs = textFileRead(frag);
	gs = textFileRead(geom);
	
	const char * vv = vs;
	const char * ff = fs;
	const char * gg = gs;
	
	glShaderSource(v, 1, &vv, NULL);
	glShaderSource(f, 1, &ff, NULL);
	glShaderSource(g, 1, &gg, NULL);
	
	free(vs); free(fs); free(gs);
	
	glCompileShader(v);
	glCompileShader(f);
	glCompileShader(g);
	
	fprintf(stderr, "vertex\n");
	printShaderLog(v);
	
	fprintf(stderr, "fragment\n");
	printShaderLog(f);
	
	fprintf(stderr, "geometry\n");
	printShaderLog(g);
	
	pro = glCreateProgram();
	glAttachShader(pro,v);
	glAttachShader(pro,f);
	glAttachShader(pro,g);
	
	// geometry shader details
	// input: GL_POINTS, GL_LINES, GL_LINES_ADJACENCY_EXT, GL_TRIANGLES, GL_TRIANGLES_ADJACENCY_EXT
	// output: GL_POINTS, GL_LINE_STRIP, GL_TRIANGLE_STRIP 
	
	glProgramParameteriEXT(pro,GL_GEOMETRY_INPUT_TYPE_EXT,GL_TRIANGLES);//GL_LINES);
	glProgramParameteriEXT(pro,GL_GEOMETRY_OUTPUT_TYPE_EXT,GL_TRIANGLES);//GL_LINE_STRIP);
	
    //something may be wrong here- need to sort this out... note changes
    int temp;
	glGetIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT,&temp);
    glProgramParameteriEXT(pro,GL_GEOMETRY_VERTICES_OUT_EXT,3);//added this to fix 'ERROR: Too many geometry output components'
	//glProgramParameteriEXT(pro,GL_GEOMETRY_VERTICES_OUT_EXT,temp); // took this out because getting 'ERROR: Too many geometry output components'
	
	glLinkProgram(pro);
    fprintf(stderr, "program log\n");
	printProgramLog(pro);
	
	return(pro);
}

// Bitmap fonts: http://www.lighthouse3d.com/tutorials/glut-tutorial/bitmap-fonts-and-orthogonal-projections/
void SubWin::renderBitmapString(float x, float y, void *font, const char *string) {
    const char *c;
    glRasterPos2f(x,y);
    for (c=string; *c != '\0'; c++) {
        glutBitmapCharacter(font, *c);
    }
}

void SubWin::setOrthographicProjection() {
    
    // switch to projection mode
	glMatrixMode(GL_PROJECTION);
    
	// save previous matrix which contains the
	//settings for the perspective projection
	glPushMatrix();
    
	// reset matrix
	glLoadIdentity();
    
	// set a 2D orthographic projection
	gluOrtho2D(0, 200, 200, 0);
    
	// switch back to modelview mode
	glMatrixMode(GL_MODELVIEW);

}

void SubWin::restorePerspectiveProjection() {
    
	glMatrixMode(GL_PROJECTION);
	// restore previous projection matrix
	glPopMatrix();
    
	// get back to modelview mode
	glMatrixMode(GL_MODELVIEW);
}

void SubWin::drawLabel()
{
    // Set projection for GUI
	setOrthographicProjection();
    
	glPushMatrix();
	glLoadIdentity();
	    
#ifndef __linux--    
	// Draw a rectangle for text
	glBegin(GL_QUADS);
	glColor4f(.3f, 0.3f, 0.3f, 0.3f);
	glVertex3f(0, 0, 0);
	glVertex3f(0, 20, 0);
	glVertex3f(90, 20, 0);
	glVertex3f(90, 0, 0);
	glEnd();
    
    glDisable(GL_BLEND);
    
    int num = 0;    
    char str[300];
    
    glColor3f(1.0, 1.0, 1.0);
    sprintf( str, "# Informed on target 1, red: %2i", numInformedTarget1);
	renderBitmapString(3, 7, GLUT_BITMAP_HELVETICA_12, (str) );
    glColor3f(1.0, 1.0, 1.0);
    sprintf( str, "# Informed on target 2, yellow: %2i", numInformedTarget2);
	renderBitmapString(3, 15, GLUT_BITMAP_HELVETICA_12, (str) );   

#endif
    
#ifdef __linux__
    // Draw a rectangle for text
	glBegin(GL_QUADS);
	glColor4f(.3f, 0.3f, 0.3f, 0.3f);
	glVertex3f(0, 0, 0);
	glVertex3f(0, 20, 0);
	glVertex3f(50, 20, 0);
	glVertex3f(50, 0, 0);
	glEnd();
    
    glDisable(GL_BLEND);
    
    int num = 0;    
    char str[300];
    
    glColor3f(1.0, 1.0, 1.0);
    sprintf( str, "# Informed on target 1, red: %2i", numInformedTarget1);
	renderBitmapString(3, 7, GLUT_BITMAP_HELVETICA_18, (str) );
    glColor3f(1.0, 1.0, 1.0);
    sprintf( str, "# Informed on target 2, yellow: %2i", numInformedTarget2);
	renderBitmapString(3, 15, GLUT_BITMAP_HELVETICA_18, (str) );   
#endif
	
    glFlush();

    glPopMatrix();
	// Restore projection for non-GUI rendering
	restorePerspectiveProjection();
    
}

void SubWin::cleanup()
{
    	if(ckKernel)clReleaseKernel(ckKernel); 
        if(cqCommandQueue)clReleaseCommandQueue(cqCommandQueue);
        if(vbo)
        {
            glBindBuffer(1, vbo);
            glDeleteBuffers(1, &vbo);
            vbo = 0;
        }
        if(vbo_cl)clReleaseMemObject(vbo_cl);
        if(cl_fishPosData)clReleaseMemObject(cl_fishPosData);
        if(cl_fishVelData)clReleaseMemObject(cl_fishVelData);
        if(cl_fishSchoolingData)clReleaseMemObject(cl_fishSchoolingData);
        if(cl_fishInformedData)clReleaseMemObject(cl_fishInformedData);
        if(cl_isInformed)clReleaseMemObject(cl_isInformed);

        if(cxGPUContext)clReleaseContext(cxGPUContext);
}



// Main program
//*****************************************************************************

int *pArgc = NULL;
char **pArgv = NULL;

//OpenGL
void initGL(int* argc, char** argv);
void framesPerSecond();

//OpenCL
void initOpenCL( int argc, char** argv );

//Fish data and functions
//const unsigned int numFish =  1024;
float fishPos[numFish][4];
float fishVel[numFish][4]; //array of float 4's to hold vx, vy, vz and nothing (for now) - 
float fishSchoolingData[numFish][4];//array of float 4's to hold coh,sep,ali,perceptualRange
bool isInformed[numFish][1];//holds true if is an informed fish.  false if not
float fishInformedData[numFish][4];// targetx, targety, targetz, strength
int createFish();

// ***** Main Window *****//
int mainwindow = 0; 

float main_window_x = 0;
float main_window_y = 0;
float main_window_w = 1024;//256 + GAP * 2;
float main_window_h = 768;//256 + 64 + GAP * 3;

void displayMain();
void keyboard(unsigned char key, int x, int y);
void special( int key, int x, int y );
void mouse(int button, int state, int x, int y);
void mouseWheel(int button, int dir, int x, int y);
void motion(int x, int y);
void timerEvent(int value);
void reshape(int w, int h);

void reset();

// ***** Sub Window 1 *****//
SubWin* subwindow1;

float subwindow1_x = 25;//GAP;
float subwindow1_y = 25;//GAP;
float subwindow1_w = 350;//1024/2-2*GAP;
float subwindow1_h = 350;//768/2-2*GAP;

int subwindow_1;//subwindow id

// ***** Sub Window 2 *****//
SubWin* subwindow2;

float subwindow2_x = 375;//2*GAP+1024/2;
float subwindow2_y = 25;//2*GAP+768/2;
float subwindow2_w = 350;//1024/2-2*GAP;
float subwindow2_h =  350;//1024/2-2*GAP;

int subwindow_2;//subwindow id

#ifndef __linux__
const int numSubWindowsX = 2;
const int numSubWindowsY = 2;
#endif

#ifdef __linux__
const int numSubWindowsX = 6;
const int numSubWindowsY = 3;
#endif

SubWin* subwindows[ numSubWindowsX ][numSubWindowsY];

//-------------------------------------------------------------------------
//  Redisplay contents of subwindow 1 and subwindow 2.
//-------------------------------------------------------------------------
void redisplay_all (void)
{
    glutSetWindow(subwindow_1);
    glutPostRedisplay();
    glutSetWindow(subwindow_2);
    glutPostRedisplay();
}

int main( int argc, char** argv)
{
	pArgc = &argc;
	pArgv = argv;
    
    // start logs 
    shrQAStart(argc, argv);
	cExecutableName = argv[0];
    shrSetLogFileName ("SoManyFish.txt");
    shrLog("%s Starting...\n\n", argv[0]); 

    // Initialize OpenGL items (if not No-GL QA test)
	shrLog("%sInitGL...\n\n", bQATest ? "Skipping " : "Calling "); 
    if(!bQATest)
    {
        initGL(&argc, argv);
    }
    
    std::cout << "creating fish" << std::endl;
    createFish();//fills fish buffer with data 
    
    initOpenCL(argc, argv);
    
    //NOW SETUP KERNEL AND PROGRAM for each subwindow
//    subwindow1->setupOpenCL(); 
//    subwindow2->setupOpenCL();
    for(int i = 0; i < numSubWindowsX; i++)
    {
        for(int j = 0; j < numSubWindowsY; j++)
        {
            subwindows[i][j]->setupOpenCL();
        }
    }
    
    // init timer 1 for fps measurement 
    shrDeltaT(1);  
    
    // Start main GLUT rendering loop for processing and rendering, 
	// or otherwise run No-GL Q/A test sequence
    shrLog("\n%s...\n", bQATest ? "No-GL test sequence" : "Standard GL Loop"); 
    if(!bQATest) 
    {
        glutMainLoop();
    }
    
    // Normally unused return path
    Cleanup(EXIT_SUCCESS);
}


void initOpenCL( int argc, char** argv)
{
    
    std::cout << "init opencl in main program " << std::endl;
    
    //Get the NVIDIA platform-- can be non-nvidia
    ciErrNum = oclGetPlatformID(&cpPlatform);
    oclCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
    
    // Get the number of GPU devices available to the platform
    ciErrNum = clGetDeviceIDs(cpPlatform, CL_DEVICE_TYPE_GPU, 0, NULL, &uiDevCount);//will return back one or more gpus
    oclCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
    //typically want gpu not integrated graphics.  
    
    // Create the device list
    cdDevices = new cl_device_id [uiDevCount];
    ciErrNum = clGetDeviceIDs(cpPlatform, CL_DEVICE_TYPE_GPU, uiDevCount, cdDevices, NULL);
    oclCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
    
    // Get device requested on command line, if any
    unsigned int uiDeviceUsed = 0;
    unsigned int uiEndDev = uiDevCount - 1;
    if(shrGetCmdLineArgumentu(argc, (const char**)argv, "device", &uiDeviceUsed ))
    {
        uiDeviceUsed = CLAMP(uiDeviceUsed, 0, uiEndDev);
        uiEndDev = uiDeviceUsed; 
    } 
    
    // Check if the requested device (or any of the devices if none requested) supports context sharing with OpenGL
    if(!bQATest)
    {
        bool bSharingSupported = false;
        for(unsigned int i = uiDeviceUsed; (!bSharingSupported && (i <= uiEndDev)); ++i) 
        {
            size_t extensionSize;
            ciErrNum = clGetDeviceInfo(cdDevices[i], CL_DEVICE_EXTENSIONS, 0, NULL, &extensionSize );
            oclCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
            if(extensionSize > 0) 
            {
                char* extensions = (char*)malloc(extensionSize);
                ciErrNum = clGetDeviceInfo(cdDevices[i], CL_DEVICE_EXTENSIONS, extensionSize, extensions, &extensionSize);
                oclCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
                std::string stdDevString(extensions);
                free(extensions);
                
                size_t szOldPos = 0;
                size_t szSpacePos = stdDevString.find(' ', szOldPos); // extensions string is space delimited
                while (szSpacePos != stdDevString.npos)
                {
                    if( strcmp(GL_SHARING_EXTENSION, stdDevString.substr(szOldPos, szSpacePos - szOldPos).c_str()) == 0 ) 
                    {
                        // Device supports context sharing with OpenGL
                        uiDeviceUsed = i;
                        bSharingSupported = true;
                        break;
                    }
                    do 
                    {
                        szOldPos = szSpacePos + 1;
                        szSpacePos = stdDevString.find(' ', szOldPos);
                    } 
                    while (szSpacePos == szOldPos);
                }
            }
        }
        
        shrLog("%s...\n\n", bSharingSupported ? "Using CL-GL Interop" : "No device found that supports CL/GL context sharing");  
        oclCheckErrorEX(bSharingSupported, true, pCleanup);
        
        // Define OS-specific context properties and create the OpenCL context
#if defined (__APPLE__)
        CGLContextObj kCGLContext = CGLGetCurrentContext();
        CGLShareGroupObj kCGLShareGroup = CGLGetShareGroup(kCGLContext);
        cl_context_properties props[] = 
        {
            CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE, (cl_context_properties)kCGLShareGroup, 
            0 
        };
        cxGPUContext = clCreateContext(props, 0,0, NULL, NULL, &ciErrNum);
#else
#ifdef UNIX
        cl_context_properties props[] = 
        {
            CL_GL_CONTEXT_KHR, (cl_context_properties)glXGetCurrentContext(), 
            CL_GLX_DISPLAY_KHR, (cl_context_properties)glXGetCurrentDisplay(), 
            CL_CONTEXT_PLATFORM, (cl_context_properties)cpPlatform, 
            0
        };
        cxGPUContext = clCreateContext(props, 1, &cdDevices[uiDeviceUsed], NULL, NULL, &ciErrNum);
#else // Win32
        cl_context_properties props[] = 
        {
            CL_GL_CONTEXT_KHR, (cl_context_properties)wglGetCurrentContext(), 
            CL_WGL_HDC_KHR, (cl_context_properties)wglGetCurrentDC(), 
            CL_CONTEXT_PLATFORM, (cl_context_properties)cpPlatform, 
            0
        };
        cxGPUContext = clCreateContext(props, 1, &cdDevices[uiDeviceUsed], NULL, NULL, &ciErrNum);
#endif
#endif
    }
    else 
    {
        cl_context_properties props[] = {CL_CONTEXT_PLATFORM, (cl_context_properties)cpPlatform, 0};
        cxGPUContext = clCreateContext(props, 1, &cdDevices[uiDeviceUsed], NULL, NULL, &ciErrNum);
    }
    
    shrCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);//if problem , bug out
    
    // Log device used (reconciled for requested requested and/or CL-GL interop capable devices, as applies)
    shrLog("Device # %u, ", uiDeviceUsed);
    oclPrintDevName(LOGBOTH, cdDevices[uiDeviceUsed]); // expect to get GPU
    shrLog("\n");
    
    //command queue was here
    
    
    // Kernel Program Setup
    string pathToKernel = "p3.cl";
    
    //#ifdef __linux__
    //    std::cout << "linux!" << std::endl;
    //    pathToKernel = "/home/evl/jauri/NVIDIA_GPU_Computing_SDK/OpenCL/src/so-many-fish/p3.cl";
    //#endif 
    
    size_t program_length;
    cPathAndName = shrFindFilePath(pathToKernel.c_str(), argv[0]);// get kernel file
    shrCheckErrorEX(cPathAndName != NULL, shrTRUE, pCleanup);
    cSourceCL = oclLoadProgSource(cPathAndName, "", &program_length);
    shrCheckErrorEX(cSourceCL != NULL, shrTRUE, pCleanup);
    
    // create the program
    cpProgram = clCreateProgramWithSource(cxGPUContext, 1,
                                          (const char **) &cSourceCL, &program_length, &ciErrNum);
    shrCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
    
    // build the program
    ciErrNum = clBuildProgram(cpProgram, 0, NULL, NULL, NULL, NULL);
    if (ciErrNum != CL_SUCCESS)
    {
        // write out standard error, Build Log and PTX, then cleanup and exit
        shrLogEx(LOGBOTH | ERRORMSG, ciErrNum, STDERROR);
        oclLogBuildInfo(cpProgram, oclGetFirstDev(cxGPUContext));
        oclLogPtx(cpProgram, oclGetFirstDev(cxGPUContext), "soManyFish.ptx");
        Cleanup(EXIT_FAILURE); 
    }
    
    //before exiting- tell the subwindow objects which device to use
    //    subwindow1->setUIDeviceUsed( uiDeviceUsed );
    //    subwindow2->setUIDeviceUsed( uiDeviceUsed );
    for(int i = 0; i < numSubWindowsX; i++)
    {
        for(int j = 0; j < numSubWindowsY; j++)
        {
            subwindows[i][j]->setUIDeviceUsed( uiDeviceUsed );
            //subwindows[i][j]->setContextUsed( (i*j+j) % uiDevCount );
        }
    }
    
    
    std::cout << "done with opencl init" << std::endl;
    
    
    
}

void initGL(int* argc, char** argv)//set up gl
{
    eyeX = 1000.0;//(float)(maxX-minX)/2;
    eyeY = 1000.0;//(float)(maxY-minY)/2;
    eyeZ = maxZ;//(float)2700.0;//750.0;
    minEyeX = -eyeX;//(float)minX;
    minEyeY = -eyeY;//(float)minY;
    minEyeZ = 50.0;
    minEyeX = eyeX;//(float)maxX;
    maxEyeY =  eyeY;//(float)maxY;
    maxEyeZ = eyeZ*5.0;//750.0; 
    
    deltaX = 0.0;
    deltaY = 0.0;
    deltaZ = 0.0;

    /**** Main window *****/
    glutInit(argc, argv);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
    glutInitWindowPosition (0, 0);//glutGet(GLUT_SCREEN_WIDTH)/2 - window_width/2, 
                            //glutGet(GLUT_SCREEN_HEIGHT)/2 - window_height/2);
    glutInitWindowSize(window_width, window_height);
    mainwindow = glutCreateWindow("Project 3: So Many Fishes ");
#if !(defined (__APPLE__) || defined(MACOSX))
    glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS);
#endif
    glutDisplayFunc(displayMain);
    glutKeyboardFunc(keyboard);
    glutMouseFunc(mouse);
    glutMotionFunc(motion);
    //glutMouseWheelFunc(mouseWheel);
	glutTimerFunc(REFRESH_DELAY, timerEvent,0);
    glutSpecialFunc( special );
    glutReshapeFunc( reshape );
    
    
    //****SETUP subwindows (truly are viewports with separate kernels and data sets******//
    
    int subWindowWidth = (int)(( window_width - (numSubWindowsX+1)*GAP ) / numSubWindowsX);
    int subWindowHeight = (int)(( window_height - (numSubWindowsY+1)*GAP ) / numSubWindowsY); 
    int runningX = (int)GAP;
    int runningY = (int)GAP;
    for(int i = 0 ; i < numSubWindowsX; i++)
    {
        for(int j = 0; j < numSubWindowsY; j++)
        {
            subwindows[i][j] = new SubWin( (int)mainwindow, i*numSubWindowsY+j, i*numSubWindowsY+j, (int)runningX, (int)runningY, (int)subWindowWidth, (int)subWindowHeight );
            runningY+= GAP + subWindowHeight;
        }
        runningX+= (int)(GAP + subWindowWidth);
        runningY = (int)GAP;
    }
    
    
    // initialize necessary OpenGL extensions
    glewInit();
    GLboolean bGLEW = glewIsSupported("GL_VERSION_2_0 GL_ARB_pixel_buffer_object"); 
    shrCheckErrorEX(bGLEW, shrTRUE, pCleanup);

}

void reset()
{
    createFish();
    
    for(int i = 0; i < numSubWindowsX; i++)
    {
        for(int j = 0; j < numSubWindowsY; j++)
        {
            subwindows[i][j]->resetCLData();
        }
    }
    
}

//********************************** PROGRAM FUNCTIONS *************************************
////////////////////////////////////////////////////////////////
//INIT:
//float fishVel[numFish][4]; //array of float 4's to hold vx, vy, vz and nothing (for now) - 
//float fishSchoolingData[numFish][4];//array of float 4's to hold coh,sep,ali,perceptualRange
//bool isInformed[numFish][1];//holds true if is an informed fish.  false if not
//float fishInformedData[numFish][4];// targetx, targety, targetz, strength

//later init position
//        fish[i][0] = rand() % ( maxX-minX ) + minX;//assign rand x coord, btw 50 and winWidth-50
//        fish[i][1] = rand() % ( maxY-minY) + minY;//same for y
int createFish(void)
{
    std::cout << "create fish " << numFish << std::endl;
    //for random number generation
    srand ( time(NULL) );
    
    target1[0] = 100.0;
    target1[1] = 100.0;
    target1[2] = 100.0;
    target1[3] = 1.0;
    
    target2[0] = 1900.0;
    target2[1] = 1900.0;
    target2[2] = 1900.0;
    target2[3] = 1.0;
    
    
    for(int i = 0; i < numFish; i++)
    {
        fishPos[i][0] = (rand() % 100 ) + 1050.0 - 50.0;
        fishPos[i][1] = (rand() % 100 ) + 1050.0 - 50.0;
        fishPos[i][2] = (rand() % 100 ) + 1050.0 - 50.0;
        fishPos[i][3] = 1.0;
        
        fishVel[i][0] = ((rand() % 10 ))*.1;// + .25; //TEST
        fishVel[i][1] = ((rand() % 10 ))*.1;// + .25;
        fishVel[i][2] = ((rand() % 10 ))*.1;// + .25;
        fishVel[i][3] = 0.0; //wv
        
        fishSchoolingData[i][0] = .001;//.001; //cohesion
        fishSchoolingData[i][1] = .016;//.016; //alignment
        fishSchoolingData[i][2] = 1.0; //separation
        fishSchoolingData[i][3] = 3.0; //perceptual range- number of body lengths
        
        isInformed[i][0] = ( rand() % 2 == 0 ); // 50% are informed
        
        //NOTE- not sending this anymore
        if( i % 1000 == 0 )//isInformed[i][0] )
        {
            fishInformedData[i][0] = target1[0];//500.0;//tz
            fishInformedData[i][1] = target1[1];//500.0;//ty
            fishInformedData[i][2] = target1[2];//500.0;//tx
            fishInformedData[i][3] = .0001; //strength
        }
        else
        {
            fishInformedData[i][0] = -1.0;//tz
            fishInformedData[i][1] = -1.0;//ty
            fishInformedData[i][2] = -1.0;//tx
            fishInformedData[i][3] = 0.0; //strength
        }
    }
    
    //add these fish to the subwindow
    float percInformed1 = (float)minInformedX;
    float percInformed2 = (float)minInformedY;
    float incr1 = ( (float)maxInformedX-(float)minInformedX );
    if( numSubWindowsX > 1)
        incr1 = ( (float)maxInformedX-(float)minInformedX ) / ( (float)numSubWindowsX -1.0 );
    float incr2 = ( (float)maxInformedY-(float)minInformedY );// 
    if( numSubWindowsY > 1 )
        incr2 = ( (float)maxInformedY-(float)minInformedY ) / ( (float)numSubWindowsY -1.0 );

    for(int i = 0; i < numSubWindowsX; i++)
    {
        for(int j = 0; j < numSubWindowsY; j++)
        {
            cout << "i = " << i << " j = " << j << " " << percInformed1 << " " << percInformed2 << endl;
            subwindows[i][j]->createFish( fishPos, fishVel, fishSchoolingData, percInformed1, percInformed2 );//isInformed, fishInformedData );
            percInformed2 += incr2;
        }
        percInformed2 = minInformedY;
        percInformed1 += incr1;
    }
    
    return 1;
}


//******************************** DRAW CONTROL PANEL *********************************

// Bitmap fonts: http://www.lighthouse3d.com/tutorials/glut-tutorial/bitmap-fonts-and-orthogonal-projections/
void renderBitmapString(float x, float y, void *font, const char *string) {
    const char *c;
    glRasterPos2f(x,y);
    for (c=string; *c != '\0'; c++) {
        glutBitmapCharacter(font, *c);
    }
}

void setOrthographicProjection() {
    
	// switch to projection mode
	glMatrixMode(GL_PROJECTION);
    
	// save previous matrix which contains the
	//settings for the perspective projection
	glPushMatrix();
    
	// reset matrix
	glLoadIdentity();
    
	// set a 2D orthographic projection
	gluOrtho2D(0, 200, 200, 0);
    
	// switch back to modelview mode
	glMatrixMode(GL_MODELVIEW);
}

void restorePerspectiveProjection() {
    
	glMatrixMode(GL_PROJECTION);
	// restore previous projection matrix
	glPopMatrix();
    
	// get back to modelview mode
	glMatrixMode(GL_MODELVIEW);
}

void drawControlPanel()
{
    // Set projection for GUI
	setOrthographicProjection();
    
#ifndef __linux--    
	// Draw a rectangle for text
	glBegin(GL_QUADS);
	glColor4f(0.0f, 0.0f, 0.0f, 0.8f);
	glVertex3f(0, 0, 0);
	glVertex3f(0, 286, 0);
	glVertex3f(256, 286, 0);
	glVertex3f(256, 0, 0);
	glEnd();
    
	glDisable(GL_BLEND);
    
    int num = 0;    
    char str[300];
    
    glColor3f(1.0, 0.0, 0.0);
	renderBitmapString(10, 4,GLUT_BITMAP_HELVETICA_18, ("Reset Program With New Values") );
    
	glColor3f(1.0, 1.0, 1.0);
    sprintf( str, "X Axis: Min informed on target 1: %2i percent", minInformedX);
	renderBitmapString(10, 24,GLUT_BITMAP_HELVETICA_18, (str) );
    sprintf( str, "X Axis: Max informed on target 1: %2i percent", maxInformedX);
	renderBitmapString(10, 44,GLUT_BITMAP_HELVETICA_18, (str) );
    sprintf( str, "Y Axis: Min informed on target 2: %2i percent", minInformedY);
	renderBitmapString(10, 64,GLUT_BITMAP_HELVETICA_18, (str) );
    sprintf( str, "Y Axis: Max informed on target 2: %2i percent", maxInformedY);
	renderBitmapString(10, 84,GLUT_BITMAP_HELVETICA_18, (str) );
    
    int endOfText = 170;
    
    //draw up down buttons
    glBegin( GL_TRIANGLES );
    
        glColor4f( 1.0, 1.0, 0.0, 1.0 );
        //UP ARROWS
        glVertex3f( endOfText, 24, 0 );
        glVertex3f( endOfText+5, 14, 0 );
        glVertex3f( endOfText+10, 24, 0 );
    
        glVertex3f( endOfText, 44, 0 );
        glVertex3f( endOfText+5, 34, 0 );
        glVertex3f( endOfText+10, 44, 0 );
    
        glVertex3f( endOfText, 64, 0 );
        glVertex3f( endOfText+5, 54, 0 );
        glVertex3f( endOfText+10, 64, 0 );
        
        glVertex3f( endOfText, 84, 0 );
        glVertex3f( endOfText+5, 74, 0 );
        glVertex3f( endOfText+10, 84, 0 );
    
        //DOWN ARROWS
        glColor4f( 0.0, 0.0, 1.0, 1.0 );
        glVertex3f( endOfText+15, 14, 0 );
        glVertex3f( endOfText+20, 24, 0 );
        glVertex3f( endOfText+25, 14, 0 );
        
        glVertex3f( endOfText+15, 34, 0 );
        glVertex3f( endOfText+20, 44, 0 );
        glVertex3f( endOfText+25, 34, 0 );
        
        glVertex3f( endOfText+15, 54, 0 );
        glVertex3f( endOfText+20, 64, 0 );
        glVertex3f( endOfText+25, 54, 0 );
    
        glVertex3f( endOfText+15, 74, 0 );
        glVertex3f( endOfText+20, 84, 0 );
        glVertex3f( endOfText+25, 74, 0 );
    
    glEnd();
    
#endif
    
#ifdef __linux__
    // Draw a rectangle for text
	glBegin(GL_QUADS);
	glColor4f(0.0f, 0.0f, 0.0f, 0.8f);
	glVertex3f(0, 0, 0);
	glVertex3f(0, 286, 0);
	glVertex3f(256, 286, 0);
	glVertex3f(256, 0, 0);
	glEnd();
    
	glDisable(GL_BLEND);
    
    int num = 0;    
    char str[300];
    
    glColor3f(1.0, 0.0, 0.0);
	renderBitmapString(100, 14,GLUT_BITMAP_HELVETICA_18, ("Reset Program With New Values") );
    
	glColor3f(1.0, 1.0, 1.0);
    sprintf( str, "X Axis: Min informed on target 1: %2i percent", minInformedX);
	renderBitmapString(100, 44,GLUT_BITMAP_HELVETICA_18, (str) );
    sprintf( str, "X Axis: Max informed on target 1: %2i percent", maxInformedX);
	renderBitmapString(100, 64,GLUT_BITMAP_HELVETICA_18, (str) );
    sprintf( str, "Y Axis: Min informed on target 2: %2i percent", minInformedY);
	renderBitmapString(100, 84,GLUT_BITMAP_HELVETICA_18, (str) );
    sprintf( str, "Y Axis: Max informed on target 2: %2i percent", maxInformedY);
	renderBitmapString(100, 104,GLUT_BITMAP_HELVETICA_18, (str) );
    
    int endOfText = 170;
    
    //draw up down buttons
    glBegin( GL_TRIANGLES );
    
    glColor4f( 1.0, 1.0, 0.0, 1.0 );
    //UP ARROWS
    glVertex3f( endOfText, 44, 0 );
    glVertex3f( endOfText+5, 34, 0 );
    glVertex3f( endOfText+10, 44, 0 );
    
    glVertex3f( endOfText, 64, 0 );
    glVertex3f( endOfText+5, 54, 0 );
    glVertex3f( endOfText+10, 64, 0 );
    
    glVertex3f( endOfText, 84, 0 );
    glVertex3f( endOfText+5, 74, 0 );
    glVertex3f( endOfText+10, 84, 0 );
    
    glVertex3f( endOfText, 104, 0 );
    glVertex3f( endOfText+5, 94, 0 );
    glVertex3f( endOfText+10, 104, 0 );
    
    //DOWN ARROWS
    glColor4f( 0.0, 0.0, 1.0, 1.0 );
    glVertex3f( endOfText+15, 34, 0 );
    glVertex3f( endOfText+20, 44, 0 );
    glVertex3f( endOfText+25, 34, 0 );
    
    glVertex3f( endOfText+15, 54, 0 );
    glVertex3f( endOfText+20, 64, 0 );
    glVertex3f( endOfText+25, 54, 0 );
    
    glVertex3f( endOfText+15, 74, 0 );
    glVertex3f( endOfText+20, 84, 0 );
    glVertex3f( endOfText+25, 74, 0 );
    
    glVertex3f( endOfText+15, 94, 0 );
    glVertex3f( endOfText+20, 104, 0 );
    glVertex3f( endOfText+25, 94, 0 );
    
    glEnd();
#endif
	
    
	glPopMatrix();
	// Restore projection for non-GUI rendering
	restorePerspectiveProjection();
    
}




//********************************** MAIN WINDOW FUNCTIONS **********************************
void displayMain() //computation linked to graphics refresh- better to have running separately
{
    framesPerSecond();
    
    //glutSetWindow( mainwindow ); 
    
    // default initialization
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glDisable(GL_DEPTH_TEST);
    // set view matrix
    glClear(GL_COLOR_BUFFER_BIT);
    
    for(int i = 0; i < numSubWindowsX; i++)
    {
        for(int j = 0; j < numSubWindowsY; j++)
        {
            subwindows[i][j]->display();
        }
    }
    
    glPushMatrix();
	glLoadIdentity();
    
    if( drawMenu )
        drawControlPanel();
    
    // flip backbuffer to screen
    glutSwapBuffers();
    glutPostRedisplay();
    
}

void reshape(int width, int height)
{
    
    //  Just take the case when the user tries
	//  to make the size of the window very small...
	if (width < GAP * 4 || height < GAP * 6)
	{
		//glutSetWindow (main_window);
		glutReshapeWindow (main_window_w, main_window_h);
		return;
	}
	
	//  Change the subwindow 1 dimensions as window dimensions change
	//  main_window_w          ---> subwindow1_w
	//  main_window_w' (width) ---> ??
	//  ==> 
	subwindow1_w = (subwindow1_w * (width-GAP*2.0))/(main_window_w-GAP*2.0);
	subwindow1_h = (subwindow1_h * (height-GAP*3.0))/(main_window_h-GAP*3.0);
    
	//  Set subwindow 1 as current window and then reposition and resize it
//    subwindow1->reshape(subwindow1_w, subwindow1_h);
	
	//  Change the subwindow 2 dimensions as window dimensions change
	subwindow2_w = (subwindow2_w * (width-GAP*2.0))/(main_window_w-GAP*2.0);
	subwindow2_h = (subwindow2_h * (height-GAP*3.0))/(main_window_h-GAP*3.0);
    
	//  Set subwindow 2 as current window and then reposition and resize it
//    subwindow2->reshape(subwindow2_w, subwindow2_h);
    
	//  Stay updated with the window width and height
	main_window_w = width;
	main_window_h = height;
    
	//  Print current width and height on the screen
	printf ("Width: %d, Height: %d.\n", width, height);


}

void timerEvent(int value)
{
    glutPostRedisplay();
	glutTimerFunc(REFRESH_DELAY, timerEvent,0);
}

void keyboard(unsigned char key, int x, int y)
{
    switch(key) 
    {
        case '\033': // escape quits 0
            // Cleanup up and quit
            bNoPrompt = shrTRUE;
	        Cleanup(EXIT_SUCCESS);
            break;
        case 'm' :
            drawMenu = !drawMenu;
            break;
        case 'r':
            reset();
        case '+' :
            deltaZ += 100.0;
            break;
        case '=' :
            deltaZ += 100.0;
            break;
        case '-' :
            deltaZ -= 100.0;
            break;
        case '_' :
            deltaZ -= 100.0;
            break; 

        case '0' :
            velLevel = 0.0;
            if( oldVelLevel != velLevel )
            {
                changed = true;
                oldVelLevel = velLevel;
            }
            break;
        case '1' :
            velLevel = 1.0;
            if( oldVelLevel != velLevel )
            {
                changed = true;
                oldVelLevel = velLevel;
            }
            break;
        case '2':
            velLevel = 2.0;
            if( oldVelLevel != velLevel )
            {
                changed = true;
                oldVelLevel = velLevel;
            }
            break;
        case '3' :
            velLevel = 3.0;
            if( oldVelLevel != velLevel )
            {
                changed = true;
                oldVelLevel = velLevel;
            }
            break;
        case '4' :
            velLevel = 4.0;
            if( oldVelLevel != velLevel )
            {
                changed = true;
                oldVelLevel = velLevel;
            }
            break;
        case '5':
            velLevel = 5.0;
            if( oldVelLevel != velLevel )
            {
                changed = true;
                oldVelLevel = velLevel;
            }
            break;
        case '6':
            velLevel = 6.0;
            if( oldVelLevel != velLevel )
            {
                changed = true;
                oldVelLevel = velLevel;
            }
            break;
        case '7' :
            velLevel = 7.0;
            if( oldVelLevel != velLevel )
            {
                changed = true;
                oldVelLevel = velLevel;
            }
            break;
        case '8':
            velLevel = 8.0;
            if( oldVelLevel != velLevel )
            {
                changed = true;
                oldVelLevel = velLevel;
            }
            break;
        case '9':
            velLevel = 9.0;
            if( oldVelLevel != velLevel )
            {
                changed = true;
                oldVelLevel = velLevel;
            }
            break;
    }
    
    
    if( deltaZ > maxEyeZ )
        deltaZ= maxEyeZ;
    else if( deltaZ < minEyeZ )
        deltaZ = minEyeZ;
}

void checkMousePressed(int x, int y)
{
    
}

void mouse(int button, int state, int x, int y)
{
    // Wheel reports as button 3(scroll up) and button 4(scroll down)
    if ((button == 3) || (button == 4)) // It's a wheel event
    {
        // Each wheel event reports like a button click, GLUT_DOWN then GLUT_UP
        if (state == GLUT_UP) return; // Disregard redundant GLUT_UP events
        fprintf(stderr, "HI  Scroll %s At %d %d\n", (button == 3) ? "Up" : "Down", x, y);
        fprintf(stderr, "Hello!!!!!!!!! Scroll %s At %d %d\n", (button == 3) ? "Up" : "Down", x, y);
    }
    else
    {  // normal button event
        fprintf(stderr, "Button %s At %d %d\n", (state == GLUT_DOWN) ? "Down" : "Up", x, y);
        //printf("Button %s At %d %d\n", (state == GLUT_DOWN) ? "Down" : "Up", x, y);
        
#ifndef __linux__
        if( state == GLUT_DOWN )
        {
            if( drawMenu )
            {
                if( x > 922 && x < 951 ) //up buttons
                {
                    if( y > 50 && y < 67 )
                    {
                        minInformedX++;
                    }
                    if( y > 84 && y < 102 )
                    {
                        maxInformedX++;
                    }
                    if( y > 121 && y < 136 )
                    {
                        minInformedY++;
                    }
                    if( y > 155 && y < 172 )
                    {
                        maxInformedY++;
                    }
                            
                }
                if( x > 962 && x < 988 )
                {
                    if( y > 50 && y < 67 )
                    {
                        minInformedX--;
                    }
                    if( y > 84 && y < 102 )
                    {
                        maxInformedX--;
                    }
                    if( y > 121 && y < 136 )
                    {
                        minInformedY--;
                    }
                    if( y > 155 && y < 172 )
                    {
                        maxInformedY--;
                    }
                }
                
                if( x > 550 && x < 811 && y > 13 && y < 38 )
                {
                    reset();
                }
            }//draw menu
        }//glut button down
#endif
        
#ifdef __linux__
        if( state == GLUT_DOWN )
        {
            if( drawMenu )
            {
                if( x > 7960 && x < 8035 ) //up buttons
                {
                    if( y > 115 && y < 150 )
                    {
                        minInformedX++;
                    }
                    if( y > 186 && y < 225 )
                    {
                        maxInformedX++;
                    }
                    if( y > 261 && y < 300 )
                    {
                        minInformedY++;
                    }
                    if( y > 334 && y < 372 )
                    {
                        maxInformedY++;
                    }
                    
                }
                if( x > 8064 && x < 8133 )
                {
                    if( y > 115 && y < 150 )
                    {
                        minInformedX--;
                    }
                    if( y > 186 && y < 225 )
                    {
                        maxInformedX--;
                    }
                    if( y > 261 && y < 300 )
                    {
                        minInformedY--;
                    }
                    if( y > 334 && y < 372 )
                    {
                        maxInformedY--;
                    }
                }
                
                if( x > 7498 && x < 7778 && y > 21 && y < 46 )
                {
                    reset();
                }
            }//draw menu
        }//glut button down
        
        
        
#endif
    }
    
    if( minInformedX < 0 )
        minInformedX = 0;
    if( minInformedX > 100 )
        minInformedX = 100;
    if( minInformedY < 0 )
        minInformedY = 0;
    if( minInformedY > 100)
        minInformedY = 100;
    if( maxInformedX < 0 )
        maxInformedX = 0;
    if( maxInformedX > 100 )
        maxInformedX = 100;
    if( maxInformedY < 0 )
        maxInformedY = 0;
    if( maxInformedY > 100)
        maxInformedY = 100;
    
    
    if (state == GLUT_DOWN) {
        mouse_buttons |= 1<<button;
    } else if (state == GLUT_UP) {
        mouse_buttons = 0;
    }

    mouse_old_x = x;
    mouse_old_y = y;

}
void motion(int x, int y)
{
    float dx, dy;
    dx = (float)(x - mouse_old_x);
    dy = (float)(y - mouse_old_y);
    
    if (mouse_buttons & 1) {
        rotate_x += dy * 0.2f;
        rotate_y += dx * 0.2f;
    } 
    
    mouse_old_x = x;
    mouse_old_y = y;
    
    cout << "motion " << rotate_x << " " << rotate_y << endl;
    cout << "      " << dx << " " << dy << endl;
    cout << "      " << mouse_old_x << " " << mouse_old_y << endl;


}


void special (int key, int x, int y)
{
    
	switch(key) {
		case GLUT_KEY_UP :
            deltaY -= 100.0;
            break;
		case GLUT_KEY_DOWN :
            deltaY += 100.0;
            break;
		case GLUT_KEY_RIGHT :
            deltaX -= 100.0;
            break;
        case GLUT_KEY_LEFT :
            deltaX += 100.0;
            break;
	}
}

//********************************** SUB WINDOW 1 FUNCTIONS **********************************

//************************************ HELPER FUNCTIONS ****************************************

int frame = 0;
int theTime = 0;
int timebase = 0;

void framesPerSecond()
{
    frame++;
	theTime=glutGet(GLUT_ELAPSED_TIME);
    
	if (theTime - timebase > 1000) 
    {
        //print this and counts to window frame
        char str[300];
        
        //sprintf( str, "Total fish: %4i,     Frames per second: %4.2f,       Global kernel time:  %1.5f\n", numFish, frame*1000.0/(theTime-timebase), globalKernelTime );
        sprintf( str, "Frames per second: %4.2f", frame*1000.0/(theTime-timebase));
        glutSetWindowTitle(str);
	 	timebase = theTime;
		frame = 0;
	}
    
}

// Function to clean up and exit
//*****************************************************************************
void Cleanup(int iExitCode)
{
    // Cleanup allocated objects
    shrLog("\nStarting Cleanup...\n\n");
//	if(ckKernel)clReleaseKernel(ckKernel); 
    if(cpProgram)clReleaseProgram(cpProgram);
//    if(cqCommandQueue)clReleaseCommandQueue(cqCommandQueue);
//    if(vbo)
//    {
//        glBindBuffer(1, vbo);
//        glDeleteBuffers(1, &vbo);
//        vbo = 0;
//    }
//    if(vbo_cl)clReleaseMemObject(vbo_cl);
//    if(cxGPUContext)clReleaseContext(cxGPUContext);
    if(cPathAndName)free(cPathAndName);
    if(cSourceCL)free(cSourceCL);
    if(cdDevices)delete(cdDevices);
    
//    subwindow1->cleanup();
//    subwindow2->cleanup();
    for(int i = 0; i < numSubWindowsX; i++)
    {
        for(int j = 0; j < numSubWindowsY; j++)
        {
            subwindows[i][j]->cleanup();
        }
    }
    
    
    // finalize logs and leave
    shrQAFinish2(bQATest, *pArgc, (const char **)pArgv, (iExitCode == 0) ? QA_PASSED : QA_FAILED ); 
    if (bQATest || bNoPrompt)
    {
        shrLogEx(LOGBOTH | CLOSELOG, 0, "%s Exiting...\n", cExecutableName);
    }
    else 
    {
        shrLogEx(LOGBOTH | CLOSELOG, 0, "%s Exiting...\nPress <Enter> to Quit\n", cExecutableName);
#ifdef WIN32
        getchar();
#endif
    }
    exit (iExitCode);
}
















//void initOpenCL( int argc, char** argv)
//{
//    
//    int deviceNr[MAX_GPU_COUNT];
//    
//    
//    // start timer & logs 
//    shrLog("Setting up OpenCL on the Host...\n\n"); 
//    shrDeltaT(1);
//    
//    // Annotate profiling state
//#ifdef GPU_PROFILING
//    shrLog("OpenCL Profiling is enabled...\n\n"); 
//#endif
//    
//    //Get the NVIDIA platform
//    ciErrNum = oclGetPlatformID(&cpPlatform);
//    oclCheckError(ciErrNum, CL_SUCCESS);
//    shrLog("clGetPlatformID...\n"); 
//    
//    //Get the devices
//    ciErrNum = clGetDeviceIDs(cpPlatform, CL_DEVICE_TYPE_GPU, 0, NULL, &ciDeviceCount);
//    oclCheckError(ciErrNum, CL_SUCCESS);
//    cdDevices = (cl_device_id *)malloc(ciDeviceCount * sizeof(cl_device_id) );
//    ciErrNum = clGetDeviceIDs(cpPlatform, CL_DEVICE_TYPE_GPU, ciDeviceCount, cdDevices, NULL);
//    oclCheckError(ciErrNum, CL_SUCCESS);
//    shrLog("clGetDeviceIDs...\n"); 
//    
//    //Create the context
//    cxGPUContext = clCreateContext(0, ciDeviceCount, cdDevices, NULL, NULL, &ciErrNum);
//    oclCheckError(ciErrNum, CL_SUCCESS);
//    shrLog("clCreateContext...\n");
//    
//    // Set up command queue(s) for GPU's specified on the command line or all GPU's
//    if(shrCheckCmdLineFlag(argc, (const char **)argv, "device"))
//    {
//        // User specified GPUs
//        int ciMaxDeviceID = ciDeviceCount-1;
//        
//        ciDeviceCount = 0;
//        char* deviceList;
//        char* deviceStr;
//        char* next_token;
//        shrGetCmdLineArgumentstr(argc, (const char **)argv, "device", &deviceList);
//        
//#ifdef WIN32
//        deviceStr = strtok_s (deviceList," ,.-", &next_token);
//#else
//        deviceStr = strtok (deviceList," ,.-");
//#endif   
//        
//        // Create command queues for all Requested GPU's
//        while(deviceStr != NULL) 
//        {
//            // get & log device index # and name
//            deviceNr[ciDeviceCount] = atoi(deviceStr);
//            if( deviceNr[ciDeviceCount] > ciMaxDeviceID ) {
//                shrLog(" Invalid user specified device ID: %d\n", deviceNr[ciDeviceCount]);
//                return 1;
//            }
//            
//            cdDevice = oclGetDev(cxGPUContext, deviceNr[ciDeviceCount]);
//            ciErrNum = clGetDeviceInfo(cdDevice, CL_DEVICE_NAME, sizeof(cDeviceName), cDeviceName, NULL);
//            oclCheckError(ciErrNum, CL_SUCCESS);
//            shrLog(" Device %i: %s\n\n", deviceNr[ciDeviceCount], cDeviceName);
//            
//            // create a command que
//            commandQueue[ciDeviceCount] = clCreateCommandQueue(cxGPUContext, cdDevice, CL_QUEUE_PROFILING_ENABLE, &ciErrNum);
//            oclCheckError(ciErrNum, CL_SUCCESS);
//            shrLog("clCreateCommandQueue\n"); 
//            
//            ++ciDeviceCount;
//            
//#ifdef WIN32
//            deviceStr = strtok_s (NULL," ,.-", &next_token);
//#else            
//            deviceStr = strtok (NULL," ,.-");
//#endif
//        }
//        
//        free(deviceList);
//    } 
//    else 
//    {
//        // Find out how many GPU's to compute on all available GPUs
//        size_t nDeviceBytes;
//        ciErrNum = clGetContextInfo(cxGPUContext, CL_CONTEXT_DEVICES, 0, NULL, &nDeviceBytes);
//        oclCheckError(ciErrNum, CL_SUCCESS);
//        ciDeviceCount = (cl_uint)nDeviceBytes/sizeof(cl_device_id);
//        
//        for(unsigned int i = 0; i < ciDeviceCount; ++i ) 
//        {
//            // get & log device index # and name
//            deviceNr[i] = i;
//            cdDevice = oclGetDev(cxGPUContext, i);
//            ciErrNum = clGetDeviceInfo(cdDevice, CL_DEVICE_NAME, sizeof(cDeviceName), cDeviceName, NULL);
//            oclCheckError(ciErrNum, CL_SUCCESS);
//            shrLog(" Device %i: %s\n", i, cDeviceName);
//            
////            // create a command que
////            commandQueue[i] = clCreateCommandQueue(cxGPUContext, cdDevice, CL_QUEUE_PROFILING_ENABLE, &ciErrNum);
////            oclCheckError(ciErrNum, CL_SUCCESS);
////            shrLog("clCreateCommandQueue\n\n"); 
//        }
//    }
//    
//    // Load the OpenCL source code from the .cl file 
//    const char* source_path = shrFindFilePath("simpleMultiGPU.cl", argv[0]);
//    char *source = oclLoadProgSource(source_path, "", &programLength);
//    oclCheckError(source != NULL, shrTRUE);
//    shrLog("oclLoadProgSource\n"); 
//    
//    // Create the program for all GPUs in the context
//    cpProgram = clCreateProgramWithSource(cxGPUContext, 1, (const char **)&source, &programLength, &ciErrNum);
//    oclCheckError(ciErrNum, CL_SUCCESS);
//    shrLog("clCreateProgramWithSource\n"); 
//    
//    // build the program
//    ciErrNum = clBuildProgram(cpProgram, 0, NULL, "-cl-fast-relaxed-math", NULL, NULL);
//    if (ciErrNum != CL_SUCCESS)
//    {
//        // write out standard error, Build Log and PTX, then cleanup and exit
//        shrLogEx(LOGBOTH | ERRORMSG, ciErrNum, STDERROR);
//        oclLogBuildInfo(cpProgram, oclGetFirstDev(cxGPUContext));
//        oclLogPtx(cpProgram, oclGetFirstDev(cxGPUContext), "oclSimpleMultiGPU.ptx");
//        oclCheckError(ciErrNum, CL_SUCCESS); 
//    }
//    shrLog("clBuildProgram\n"); 
//
//    
//    //before exiting- tell the subwindow objects which device to use
//    //    subwindow1->setUIDeviceUsed( uiDeviceUsed );
//    //    subwindow2->setUIDeviceUsed( uiDeviceUsed );
//    for(int i = 0; i < numSubWindowsX; i++)
//    {
//        for(int j = 0; j < numSubWindowsY; j++)
//        {
//            subwindows[i][j]->setUIDeviceUsed( (i*j+j) % uiDevCount  );//uiDeviceUsed );
//            //subwindows[i][j]->setContextUsed( (i*j+j) % uiDevCount );
//        }
//    }
//    
//    
//}
//
//



void initOpenCL2(int argc, char** argv)
{
    //
    //    int deviceNr[20];//MAX_GPU_COUNT];
    //    
    //    
    //    std::cout << "init opencl in main program " << std::endl;
    //    
    //    //Get the NVIDIA platform-- can be non-nvidia
    //    ciErrNum = oclGetPlatformID(&cpPlatform);
    //    oclCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
    //    shrLog("clGetPlatformID...\n"); 
    //
    //    // Get the number of GPU devices available to the platform
    //    ciErrNum = clGetDeviceIDs(cpPlatform, CL_DEVICE_TYPE_GPU, 0, NULL, &uiDevCount);//will return back one or more gpus
    //    oclCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
    //    //typically want gpu not integrated graphics.  
    //    
    //    // Create the device list
    //    cdDevices = new cl_device_id [uiDevCount];
    //    ciErrNum = clGetDeviceIDs(cpPlatform, CL_DEVICE_TYPE_GPU, uiDevCount, cdDevices, NULL);
    //    oclCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
    //    shrLog("clGetDeviceIDs...\n"); 
    //
    //    //Create the context
    //    cxGPUContext = clCreateContext(0, uiDeviceCount, cdDevices, NULL, NULL, &ciErrNum);
    //    oclCheckError(ciErrNum, CL_SUCCESS);
    //    shrLog("clCreateContext...\n");
    //    
    ////        // Define OS-specific context properties and create the OpenCL context
    ////#if defined (__APPLE__)
    ////        CGLContextObj kCGLContext = CGLGetCurrentContext();
    ////        CGLShareGroupObj kCGLShareGroup = CGLGetShareGroup(kCGLContext);
    ////        cl_context_properties props[] = 
    ////        {
    ////            CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE, (cl_context_properties)kCGLShareGroup, 
    ////            0 
    ////        };
    ////        cxGPUContext = clCreateContext(props, 0,0, NULL, NULL, &ciErrNum);
    ////#else
    ////#ifdef UNIX
    ////        cl_context_properties props[] = 
    ////        {
    ////            CL_GL_CONTEXT_KHR, (cl_context_properties)glXGetCurrentContext(), 
    ////            CL_GLX_DISPLAY_KHR, (cl_context_properties)glXGetCurrentDisplay(), 
    ////            CL_CONTEXT_PLATFORM, (cl_context_properties)cpPlatform, 
    ////            0
    ////        };
    ////        cxGPUContext = clCreateContext(props, 1, &cdDevices[uiDeviceUsed], NULL, NULL, &ciErrNum);
    ////#else // Win32
    ////        cl_context_properties props[] = 
    ////        {
    ////            CL_GL_CONTEXT_KHR, (cl_context_properties)wglGetCurrentContext(), 
    ////            CL_WGL_HDC_KHR, (cl_context_properties)wglGetCurrentDC(), 
    ////            CL_CONTEXT_PLATFORM, (cl_context_properties)cpPlatform, 
    ////            0
    ////        };
    ////        cxGPUContext = clCreateContext(props, 1, &cdDevices[uiDeviceUsed], NULL, NULL, &ciErrNum);
    ////#endif
    ////#endif
    ////    }
    ////    else 
    ////    {
    ////        cl_context_properties props[] = {CL_CONTEXT_PLATFORM, (cl_context_properties)cpPlatform, 0};
    ////        cxGPUContext = clCreateContext(props, 1, &cdDevices[uiDeviceUsed], NULL, NULL, &ciErrNum);
    ////    }
    //    
    //    shrCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);//if problem , bug out
    //    
    ////    // Log device used (reconciled for requested requested and/or CL-GL interop capable devices, as applies)
    ////    shrLog("Device # %u, ", uiDeviceUsed);
    ////    oclPrintDevName(LOGBOTH, cdDevices[uiDeviceUsed]); // expect to get GPU
    ////    shrLog("\n");
    //
    //    // Find out how many GPU's to compute on all available GPUs
    //    size_t nDeviceBytes;
    //    ciErrNum = clGetContextInfo(cxGPUContext, CL_CONTEXT_DEVICES, 0, NULL, &nDeviceBytes);
    //    oclCheckError(ciErrNum, CL_SUCCESS);
    //    ciDeviceCount = (cl_uint)nDeviceBytes/sizeof(cl_device_id);
    //    
    //    for(unsigned int i = 0; i < ciDeviceCount; ++i ) 
    //    {
    //        // get & log device index # and name
    //        deviceNr[i] = i;
    //        cdDevice = oclGetDev(cxGPUContext, i);
    //        ciErrNum = clGetDeviceInfo(cdDevice, CL_DEVICE_NAME, sizeof(cDeviceName), cDeviceName, NULL);
    //        oclCheckError(ciErrNum, CL_SUCCESS);
    //        shrLog(" Device %i: %s\n", i, cDeviceName);
    //                
    ////        // create a command que
    ////        commandQueue[i] = clCreateCommandQueue(cxGPUContext, cdDevice, CL_QUEUE_PROFILING_ENABLE, &ciErrNum);
    ////        oclCheckError(ciErrNum, CL_SUCCESS);
    ////        shrLog("clCreateCommandQueue\n\n"); 
    //    
    //    }
    //
    //    // Kernel Program Setup
    //    string pathToKernel = "p3.cl";
    //
    //    //#ifdef __linux__
    //    //    std::cout << "linux!" << std::endl;
    //    //    pathToKernel = "/home/evl/jauri/NVIDIA_GPU_Computing_SDK/OpenCL/src/so-many-fish/p3.cl";
    //    //#endif 
    //
    //    size_t program_length;
    //    cPathAndName = shrFindFilePath(pathToKernel.c_str(), argv[0]);// get kernel file
    //    shrCheckErrorEX(cPathAndName != NULL, shrTRUE, pCleanup);
    //    cSourceCL = oclLoadProgSource(cPathAndName, "", &program_length);
    //    shrCheckErrorEX(cSourceCL != NULL, shrTRUE, pCleanup);
    //
    //
    //    // create the program
    //    cpProgram = clCreateProgramWithSource(cxGPUContext, 1,
    //                                          (const char **) &cSourceCL, &program_length, &ciErrNum);
    //    shrCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
    //
    //    // build the program
    //    ciErrNum = clBuildProgram(cpProgram, 0, NULL, NULL, NULL, NULL);
    //    if (ciErrNum != CL_SUCCESS)
    //    {
    //        // write out standard error, Build Log and PTX, then cleanup and exit
    //        shrLogEx(LOGBOTH | ERRORMSG, ciErrNum, STDERROR);
    //        oclLogBuildInfo(cpProgram, oclGetFirstDev(cxGPUContext));
    //        oclLogPtx(cpProgram, oclGetFirstDev(cxGPUContext), "soManyFish.ptx");
    //        Cleanup(EXIT_FAILURE); 
    //    }
    //
    //    //before exiting- tell the subwindow objects which device to use
    //    //    subwindow1->setUIDeviceUsed( uiDeviceUsed );
    //    //    subwindow2->setUIDeviceUsed( uiDeviceUsed );
    //    for(int i = 0; i < numSubWindowsX; i++)
    //    {
    //        for(int j = 0; j < numSubWindowsY; j++)
    //        {
    //            subwindows[i][j]->setUIDeviceUsed( (i*j+j) % uiDevCount  );//uiDeviceUsed );
    //            //subwindows[i][j]->setContextUsed( (i*j+j) % uiDevCount );
    //        }
    //    }
    //
    //    std::cout << "done with opencl init" << std::endl;
    
}

//void initOpenCL2(int argc, char** argv)
//{
//    std::cout << "init opencl in main program " << std::endl;
//    
//    //Get the NVIDIA platform-- can be non-nvidia
//    ciErrNum = oclGetPlatformID(&cpPlatform);
//    oclCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
//    
//    // Get the number of GPU devices available to the platform
//    ciErrNum = clGetDeviceIDs(cpPlatform, CL_DEVICE_TYPE_GPU, 0, NULL, &uiDevCount);//will return back one or more gpus
//    oclCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
//    //typically want gpu not integrated graphics.  
//    
//    // Create the device list
//    cdDevices = new cl_device_id [uiDevCount];
//    ciErrNum = clGetDeviceIDs(cpPlatform, CL_DEVICE_TYPE_GPU, uiDevCount, cdDevices, NULL);
//    oclCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
//    
//    // Get device requested on command line, if any
//    unsigned int uiDeviceUsed = 0;
//    unsigned int uiEndDev = uiDevCount - 1;
//    if(shrGetCmdLineArgumentu(argc, (const char**)argv, "device", &uiDeviceUsed ))
//    {
//        uiDeviceUsed = CLAMP(uiDeviceUsed, 0, uiEndDev);
//        uiEndDev = uiDeviceUsed; 
//    } 
//    
//    // Check if the requested device (or any of the devices if none requested) supports context sharing with OpenGL
//    numDevicesThatCouldBeUsed = 0;
//    if(!bQATest)
//    {
//        bool bSharingSupported = false;
//        for(unsigned int i = uiDeviceUsed; i <= uiEndDev; i++)//(!bSharingSupported && (i <= uiEndDev)); ++i) 
//        {
//            size_t extensionSize;
//            ciErrNum = clGetDeviceInfo(cdDevices[i], CL_DEVICE_EXTENSIONS, 0, NULL, &extensionSize );
//            oclCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
//            if(extensionSize > 0) 
//            {
//                char* extensions = (char*)malloc(extensionSize);
//                ciErrNum = clGetDeviceInfo(cdDevices[i], CL_DEVICE_EXTENSIONS, extensionSize, extensions, &extensionSize);
//                oclCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
//                std::string stdDevString(extensions);
//                free(extensions);
//                
//                size_t szOldPos = 0;
//                size_t szSpacePos = stdDevString.find(' ', szOldPos); // extensions string is space delimited
//                while (szSpacePos != stdDevString.npos)
//                {
//                    if( strcmp(GL_SHARING_EXTENSION, stdDevString.substr(szOldPos, szSpacePos - szOldPos).c_str()) == 0 ) 
//                    {
//                        // Device supports context sharing with OpenGL
//                        uiDeviceUsed = i;
//                        bSharingSupported = true;
//                        
//                        devicesThatCouldBeUsed[ numDevicesThatCouldBeUsed ] = i;
//                        numDevicesThatCouldBeUsed++;
//                        break;
//                    }
//                    do 
//                    {
//                        szOldPos = szSpacePos + 1;
//                        szSpacePos = stdDevString.find(' ', szOldPos);
//                    } 
//                    while (szSpacePos == szOldPos);
//                }
//            }
//        }
//        
//        shrLog("%s...\n\n", bSharingSupported ? "Using CL-GL Interop" : "No device found that supports CL/GL context sharing");  
//        oclCheckErrorEX(bSharingSupported, true, pCleanup);
//        
//        for(unsigned int i = uiDeviceUsed; i <= uiEndDev; i++)//(!bSharingSupported && (i <= uiEndDev)); ++i) 
//        {
//    
//            // Define OS-specific context properties and create the OpenCL context
//    #if defined (__APPLE__)
//            CGLContextObj kCGLContext = CGLGetCurrentContext();
//            CGLShareGroupObj kCGLShareGroup = CGLGetShareGroup(kCGLContext);
//            cl_context_properties props[] = 
//            {
//                CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE, (cl_context_properties)kCGLShareGroup, 
//                0 
//            };
//            cxGPUContext = clCreateContext(props, 0,0, NULL, NULL, &ciErrNum);
//    #else
//    #ifdef UNIX
//            cl_context_properties props[] = 
//            {
//                CL_GL_CONTEXT_KHR, (cl_context_properties)glXGetCurrentContext(), 
//                CL_GLX_DISPLAY_KHR, (cl_context_properties)glXGetCurrentDisplay(), 
//                CL_CONTEXT_PLATFORM, (cl_context_properties)cpPlatform, 
//                0
//            };
//            cxGPUContexts = clCreateContext(props, 1, &cdDevices[uiDeviceUsed], NULL, NULL, &ciErrNum);
//    #else // Win32
//            cl_context_properties props[] = 
//            {
//                CL_GL_CONTEXT_KHR, (cl_context_properties)wglGetCurrentContext(), 
//                CL_WGL_HDC_KHR, (cl_context_properties)wglGetCurrentDC(), 
//                CL_CONTEXT_PLATFORM, (cl_context_properties)cpPlatform, 
//                0
//            };
//            cxGPUContext = clCreateContext(props, 1, &cdDevices[uiDeviceUsed], NULL, NULL, &ciErrNum);
//    #endif
//    #endif
//        }
//        else 
//        {
//            cl_context_properties props[] = {CL_CONTEXT_PLATFORM, (cl_context_properties)cpPlatform, 0};
//            cxGPUContext = clCreateContext(props, 1, &cdDevices[uiDeviceUsed], NULL, NULL, &ciErrNum);
//        }
//        
//        shrCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);//if problem , bug out
//        
//        // Log device used (reconciled for requested requested and/or CL-GL interop capable devices, as applies)
//        shrLog("Device # %u, ", uiDeviceUsed);
//        oclPrintDevName(LOGBOTH, cdDevices[i] );//uiDeviceUsed]); // expect to get GPU
//        shrLog("\n");
//
//        }//if !bqatest
//        
////        
////        // Define OS-specific context properties and create the OpenCL context
////#if defined (__APPLE__)
////        CGLContextObj kCGLContext = CGLGetCurrentContext();
////        CGLShareGroupObj kCGLShareGroup = CGLGetShareGroup(kCGLContext);
////        cl_context_properties props[] = 
////        {
////            CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE, (cl_context_properties)kCGLShareGroup, 
////            0 
////        };
////        cxGPUContext = clCreateContext(props, 0,0, NULL, NULL, &ciErrNum);
////#else
////#ifdef UNIX
////        cl_context_properties props[] = 
////        {
////            CL_GL_CONTEXT_KHR, (cl_context_properties)glXGetCurrentContext(), 
////            CL_GLX_DISPLAY_KHR, (cl_context_properties)glXGetCurrentDisplay(), 
////            CL_CONTEXT_PLATFORM, (cl_context_properties)cpPlatform, 
////            0
////        };
////        cxGPUContext = clCreateContext(props, 1, &cdDevices[uiDeviceUsed], NULL, NULL, &ciErrNum);
////#else // Win32
////        cl_context_properties props[] = 
////        {
////            CL_GL_CONTEXT_KHR, (cl_context_properties)wglGetCurrentContext(), 
////            CL_WGL_HDC_KHR, (cl_context_properties)wglGetCurrentDC(), 
////            CL_CONTEXT_PLATFORM, (cl_context_properties)cpPlatform, 
////            0
////        };
////        cxGPUContext = clCreateContext(props, 1, &cdDevices[uiDeviceUsed], NULL, NULL, &ciErrNum);
////#endif
////#endif
////    }
////    else 
////    {
////        cl_context_properties props[] = {CL_CONTEXT_PLATFORM, (cl_context_properties)cpPlatform, 0};
////        cxGPUContext = clCreateContext(props, 1, &cdDevices[uiDeviceUsed], NULL, NULL, &ciErrNum);
////    }
////    
////    shrCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);//if problem , bug out
////    
////    // Log device used (reconciled for requested requested and/or CL-GL interop capable devices, as applies)
////    shrLog("Device # %u, ", uiDeviceUsed);
////    oclPrintDevName(LOGBOTH, cdDevices[uiDeviceUsed]); // expect to get GPU
////    shrLog("\n");
//
//    //command queue was here
//    
//    
//    // Kernel Program Setup
//    string pathToKernel = "p3.cl";
//    
//    //#ifdef __linux__
//    //    std::cout << "linux!" << std::endl;
//    //    pathToKernel = "/home/evl/jauri/NVIDIA_GPU_Computing_SDK/OpenCL/src/so-many-fish/p3.cl";
//    //#endif 
//    
//    size_t program_length;
//    cPathAndName = shrFindFilePath(pathToKernel.c_str(), argv[0]);// get kernel file
//    shrCheckErrorEX(cPathAndName != NULL, shrTRUE, pCleanup);
//    cSourceCL = oclLoadProgSource(cPathAndName, "", &program_length);
//    shrCheckErrorEX(cSourceCL != NULL, shrTRUE, pCleanup);
//    
//    // create the program
//    cpProgram = clCreateProgramWithSource(cxGPUContext, 1,
//                                          (const char **) &cSourceCL, &program_length, &ciErrNum);
//    shrCheckErrorEX(ciErrNum, CL_SUCCESS, pCleanup);
//    
//    // build the program
//    ciErrNum = clBuildProgram(cpProgram, 0, NULL, NULL, NULL, NULL);
//    if (ciErrNum != CL_SUCCESS)
//    {
//        // write out standard error, Build Log and PTX, then cleanup and exit
//        shrLogEx(LOGBOTH | ERRORMSG, ciErrNum, STDERROR);
//        oclLogBuildInfo(cpProgram, oclGetFirstDev(cxGPUContext));
//        oclLogPtx(cpProgram, oclGetFirstDev(cxGPUContext), "soManyFish.ptx");
//        Cleanup(EXIT_FAILURE); 
//    }
//
//    //before exiting- tell the subwindow objects which device to use
////    subwindow1->setUIDeviceUsed( uiDeviceUsed );
////    subwindow2->setUIDeviceUsed( uiDeviceUsed );
//    for(int i = 0; i < numSubWindowsX; i++)
//    {
//        for(int j = 0; j < numSubWindowsY; j++)
//        {
//            subwindows[i][j]->setUIDeviceUsed( devicesThatCouldBeUsed[ (i*j+j) % numDevicesThatCouldBeUsed ] );//uiDeviceUsed );
//        }
//    }
//    
//    std::cout << "done with opencl init" << std::endl;
//}



